
#include <animbase.h>
#include <QPointF>

#define BREAK_ASSERT(C) if(!(C))break
#define VALIDATE_DOUBLE(QSTR,DEST) {bool b;DEST=QSTR.toDouble(&b);BREAK_ASSERT(b);}

ParsedLine ParseAnimLine(QString line, bool ignoreAtValidity)
{
    ParsedLine ret { 0.0, Invalid, {} };
    auto part = line.split(',');
    QString tok;

    if(part.size() == 1) // Only one part
    {
        if(!QString("%$&").contains(part[0][0])) // If not a macro
            return ret;
        else // Is a macro, get the token identifier
            tok = part[0].toLower();
    }
    else // Is an animation statement
    {
        tok = part[1].toLower();
    }

         if(tok == "spawn") ret.Token = Spawn;
    else if(tok == "kill") ret.Token = Kill;
    else if(tok == "move") ret.Token = Move;
    else if(tok == "rotate") ret.Token = Rotate;
    else if(tok == "scale") ret.Token = Scale;
    else if(tok == "opacity") ret.Token = Opacity;
    else if(tok[0] == "%") { ret.Token = MacroTrack; ret.Fields.append(tok.remove(0, 1).trimmed()); }
    else if(tok[0] == "$") { ret.Token = MacroEntity; ret.Fields.append(tok.remove(0, 1).trimmed()); }
    else if(tok[0] == "&") { ret.Token = UseMacroEntity; ret.Fields.append(tok.remove(0, 1).trimmed()); }
    else return ret;

    if(ret.Token < _MacroBegin_)
    {
     bool isAtValid = false;
     ret.At = part[0].toDouble(&isAtValid);
     if(!ignoreAtValidity && !isAtValid)
         return ret;
    }

    for(int i = 2; i < part.size(); i++)
    {
        ret.Fields.append(part[i]);
    }

    return ret;
}



AnimOp CreateOpFromLine(ParsedLine line)
{
    SubAnim _subanim {};
    AnimOp ret {
                .At = line.At,
                .Code = InvalidOp,
                .ObjName = "",
                .Anim = _subanim,
                .Args = {},
               };
    auto& fields = line.Fields;

    switch(line.Token)
    {
    case Spawn: // At,spawn,ObjName,PicPath[,AbsX,AbsY]
        BREAK_ASSERT(fields.size() >= 2);
        ret.ObjName = fields[0];
        ret.Args.append(fields[1]);
        if(fields.length() == 4)
        {
            double x, y;
            VALIDATE_DOUBLE(fields[2], x);
            VALIDATE_DOUBLE(fields[3], y);
            ret.Anim.Dest = QPointF(x, y);
        }

        ret.Code = NewEntity;
        break;

    case Kill: // At,kill,ObjName
        BREAK_ASSERT(fields.size() == 1);
        ret.ObjName = fields[0];
        ret.Code = DeleteEntity;
        break;

    case Move: // At,move,ObjName,AbsX,AbsY,Length,[Accel]
        BREAK_ASSERT(fields.size() >= 4);
        double x, y;
        ret.ObjName = fields[0];
        VALIDATE_DOUBLE(fields[1], x);
        VALIDATE_DOUBLE(fields[2], y);
        VALIDATE_DOUBLE(fields[3], ret.Anim.Period);
        ret.Anim.Dest = QPointF(x, y);
        if(fields.size() >= 5)
        {
            switch(fields[4][0].toLatin1())
            {
            default:
            case 'L':
                ret.Anim.Accel = Linear; break;

            case 'A':
                ret.Anim.Accel = Accelerate; break;

            case 'D':
                ret.Anim.Accel = Deaccelerate; break;
            }
        }
        ret.Anim.Type = AnMove;
        ret.Code = NewAnim;
        break;

    case Rotate: // At,rotate,ObjName,Angle,Length,[Accel]
        BREAK_ASSERT(fields.size() >= 3);
        ret.ObjName = fields[0];
        VALIDATE_DOUBLE(fields[1], ret.Anim.Dest);
        VALIDATE_DOUBLE(fields[2], ret.Anim.Period);
        if(fields.size() >= 4)
        {
            switch(fields[3][0].toLatin1())
            {
            default:
            case 'L':
                ret.Anim.Accel = Linear; break;

            case 'A':
                ret.Anim.Accel = Accelerate; break;

            case 'D':
                ret.Anim.Accel = Deaccelerate; break;
            }
        }
        ret.Anim.Type = AnRotate;
        ret.Code = NewAnim;
        break;

    case Scale: // At,scale,ObjName,AbsX,AbsY,Length,[Accel]
        BREAK_ASSERT(fields.size() >= 4);
        ret.ObjName = fields[0];
        VALIDATE_DOUBLE(fields[1], x);
        VALIDATE_DOUBLE(fields[2], y);
        ret.Anim.Dest = QSizeF(x, y);
        VALIDATE_DOUBLE(fields[3], ret.Anim.Period);
        if(fields.size() >= 5)
        {
            switch(fields[4][0].toLatin1())
            {
            default:
            case 'L':
                ret.Anim.Accel = Linear; break;

            case 'A':
                ret.Anim.Accel = Accelerate; break;

            case 'D':
                ret.Anim.Accel = Deaccelerate; break;
            }
        }
        ret.Anim.Type = AnScale;
        ret.Code = NewAnim;
        break;

    case Opacity: // At,opacity,ObjName,AbsOpacity,Length,[Accel]
        BREAK_ASSERT(fields.size() >= 3);
        ret.ObjName = fields[0];
        VALIDATE_DOUBLE(fields[1], ret.Anim.Dest);
        VALIDATE_DOUBLE(fields[2], ret.Anim.Period);
        if(fields.size() >= 4)
        {
            switch(fields[3][0].toLatin1())
            {
            default:
            case 'L':
                ret.Anim.Accel = Linear; break;

            case 'A':
                ret.Anim.Accel = Accelerate; break;

            case 'D':
                ret.Anim.Accel = Deaccelerate; break;
            }
        }
        ret.Anim.Type = AnOpacity;
        ret.Code = NewAnim;
        break;

    default:
        // Invalid status, macros shouldn't end up here
        break;
    }

    return ret;
}

AnimEntity::~AnimEntity()
{
    if(PosInterpolator) delete PosInterpolator;
    if(RotationInterpolator) delete RotationInterpolator;
    if(SizeInterpolator) delete SizeInterpolator;
    if(OpacityInterpolator) delete OpacityInterpolator;
}
